﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using RoslynProxy.Core.Contracts;
using System.Collections.Generic;
using System.Linq;

namespace RoslynProxy.SourceGenerator
{
    /// <summary>
    /// 代理服务类型构建器
    /// </summary>
    public class RoslynProxyServiceTypeBuilder
    {
        public bool TryBuildSource(Compilation compilation, out ProxyServiceTypeInfo typeInfo)
        {
            if (!TryGetProxyServiceTypeBuilderInfo(compilation, out var info))
            {
                typeInfo = null!;
                return false;
            }

            // 获取所有注册服务语句
            var invocationExpressions = info.ProxyServices.Select(
                CreateInvocationExpressions);

            // 注册服务方法体
            var methodBlock = SyntaxFactory.Block(
                SyntaxFactory.SeparatedList<StatementSyntax>(invocationExpressions));

            // 创建 RegisterProxyServices 方法的参数
            var serviceParameterList = SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList<ParameterSyntax>().Add(SyntaxFactory.Parameter(attributeLists: SyntaxFactory.List<AttributeListSyntax>(),
             modifiers: SyntaxFactory.TokenList(),
             SyntaxFactory.IdentifierName(
                 SyntaxFactory.Identifier(SyntaxFactory.TriviaList(),
                 "Microsoft.Extensions.DependencyInjection.IServiceCollection",
                 SyntaxFactory.TriviaList().Add(SyntaxFactory.Whitespace(" ")))),
             SyntaxFactory.Identifier("services"),
             null)));

            // 声明 RegisterProxyServices 方法
            var method = SyntaxFactory.MethodDeclaration(
                attributeLists: SyntaxFactory.List<AttributeListSyntax>(),
                modifiers: SyntaxFactory.TokenList()
                    .Add(SyntaxFactory.Token(SyntaxFactory.TriviaList(), SyntaxKind.PublicKeyword, SyntaxFactory.TriviaList().Add(SyntaxFactory.Whitespace(" "))))
                    .Add(SyntaxFactory.Token(SyntaxFactory.TriviaList(), SyntaxKind.OverrideKeyword, SyntaxFactory.TriviaList().Add(SyntaxFactory.Whitespace(" ")))),
                returnType: SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxFactory.TriviaList(), SyntaxKind.VoidKeyword, SyntaxFactory.TriviaList().Add(SyntaxFactory.Whitespace(" ")))),
                explicitInterfaceSpecifier: default!,
                identifier: SyntaxFactory.Identifier("RegisterProxyServices"),
                typeParameterList: default!,
                parameterList: serviceParameterList,
                constraintClauses: default,
                body: methodBlock,
                semicolonToken: SyntaxFactory.Token(SyntaxKind.None));

            // 声明注册服务分布类
            var myClass = SyntaxFactory.ClassDeclaration(
                attributeLists: SyntaxFactory.List<AttributeListSyntax>(),
                 modifiers: SyntaxFactory.TokenList()
                    .Add(SyntaxFactory.Token(SyntaxFactory.TriviaList(), SyntaxKind.PublicKeyword, SyntaxFactory.TriviaList().Add(SyntaxFactory.Whitespace(" "))))
                    .Add(SyntaxFactory.Token(SyntaxFactory.TriviaList(), SyntaxKind.PartialKeyword, SyntaxFactory.TriviaList().Add(SyntaxFactory.Whitespace(" ")))),
                 identifier: SyntaxFactory.Identifier(SyntaxFactory.TriviaList().Add(SyntaxFactory.Whitespace(" ")), info.RoslynProxyServiceCollectionTypeName, default),
                 typeParameterList: default,
                 baseList: default,
                 constraintClauses: default,
                 members: SyntaxFactory.List<MemberDeclarationSyntax>().Add(method)
                 );

            NamespaceDeclarationSyntax? nameSpace = null;

            // 如果是全局名称空间，那么 ContainingNamespace 为空
            if (info.ContainingNamespace != string.Empty)
            {
                // 创建类型的名称空间
                nameSpace = SyntaxFactory.NamespaceDeclaration(
                    namespaceKeyword: SyntaxFactory.Token(default, SyntaxKind.NamespaceKeyword, SyntaxFactory.TriviaList().Add(SyntaxFactory.Whitespace(" "))),
                    name: SyntaxFactory.IdentifierName(info.ContainingNamespace),
                    openBraceToken: SyntaxFactory.Token(SyntaxKind.OpenBraceToken),
                    externs: default,
                    usings: default,
                    members: SyntaxFactory.List<MemberDeclarationSyntax>().Add(myClass),
                    closeBraceToken: SyntaxFactory.Token(SyntaxKind.CloseBraceToken),
                    semicolonToken: SyntaxFactory.Token(SyntaxKind.SemicolonToken));
            }

            // 一个文件视为一个编译单元
            var unit = SyntaxFactory.CompilationUnit(
                externs: default,
                usings: SyntaxFactory.List<UsingDirectiveSyntax>().Add(SyntaxFactory.UsingDirective(default!,
                SyntaxFactory.IdentifierName(SyntaxFactory.Identifier(SyntaxFactory.TriviaList().Add(SyntaxFactory.Whitespace(" ")), "Microsoft.Extensions.DependencyInjection", default)))),
                attributeLists: default,
                members: SyntaxFactory.List<MemberDeclarationSyntax>().Add(nameSpace != null ? nameSpace : myClass));

            typeInfo = new ProxyServiceTypeInfo(info.RoslynProxyServiceCollectionTypeName, unit.ToFullString());
            return true;
        }

        /// <summary>
        /// 创建注册服务语句
        /// </summary>
        /// <param name="serviceInterface">服务接口</param>
        /// <param name="proxyType">代理类型</param>
        /// <param name="scope">服务作用域</param>
        /// <returns>返回注册服务语句</returns>
        private BlockSyntax CreateInvocationExpressions(ProxyServiceInfo info)
        {
            // 创建注册服务语句
            // 类似 services.AddTransient<MyOtherService>();
            var addTransientAccess = SyntaxFactory.MemberAccessExpression(
               SyntaxKind.SimpleMemberAccessExpression,
               SyntaxFactory.IdentifierName("services"),
               SyntaxFactory.GenericName(SyntaxFactory.Identifier($"AddTransient"),
                SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList<TypeSyntax>().Add(SyntaxFactory.IdentifierName(info.InstanceType)))));
            var addInstanceTypeinvocation = SyntaxFactory.InvocationExpression(addTransientAccess);
            var statement = SyntaxFactory.ExpressionStatement(addInstanceTypeinvocation);

            // 类似 services.AddTransient(RoslynProxy.Builder.MyOtherServiceProxy.GetProxyService);
            var methodAccess = SyntaxFactory.MemberAccessExpression(
                SyntaxKind.SimpleMemberAccessExpression,
                SyntaxFactory.IdentifierName(info.ProxyServiceType),
                SyntaxFactory.IdentifierName("GetProxyService"));

            var serviceAccess = SyntaxFactory.MemberAccessExpression(
               SyntaxKind.SimpleMemberAccessExpression,
               SyntaxFactory.IdentifierName("services"),
               SyntaxFactory.IdentifierName($"Add{info.Scope}"));

            var invocation = SyntaxFactory.InvocationExpression(
                serviceAccess,
                SyntaxFactory.ArgumentList(
                    SyntaxFactory.SeparatedList<ArgumentSyntax>()
                    .Add(SyntaxFactory.Argument(methodAccess))
                    )
                );

            return SyntaxFactory.Block(SyntaxFactory.ExpressionStatement(addInstanceTypeinvocation), SyntaxFactory.ExpressionStatement(invocation));
        }

        private readonly string ScopedRoslynProxyAttributeName = "RoslynProxy.Core.RoslynProxyAttributes.ScopedRoslynProxyAttribute";
        private readonly string SingletonRoslynProxyAttributeName = "RoslynProxy.Core.RoslynProxyAttributes.SingletonRoslynProxyAttribute";

        /// <summary>
        /// 尝试获取类型构建信息
        /// </summary>
        /// <param name="compilation">编译单元</param>
        public bool TryGetProxyServiceTypeBuilderInfo(Compilation compilation, out ProxyServiceTypeBuildInfo buildInfo)
        {
            var proxyServices = new List<ProxyServiceInfo>();
            bool hasCollection = false;
            var containingNamespace = string.Empty;
            var className = string.Empty;

            foreach (var tree in compilation.SyntaxTrees)
            {
                var root = tree.GetCompilationUnitRoot();
                var nodes = root.DescendantNodes();
                var classNodes = root.DescendantNodes().OfType<ClassDeclarationSyntax>();
                var model = compilation.GetSemanticModel(tree);

                foreach (var classNode in classNodes)
                {
                    var typeSymbol = model.GetDeclaredSymbol(classNode);

                    if (typeSymbol != null)
                    {
                        if (typeSymbol.IsAbstract || typeSymbol.IsGenericType)
                        {
                            continue;
                        }

                        // 获取代理服务信息
                        if (TryGetProxyServiceInfo(typeSymbol, out var info))
                        {
                            proxyServices.Add(info);
                        }
                        else
                        if (!hasCollection)
                        {
                            // 必须是派生自 RoslynProxyServiceCollection 类型的子类，并且携带关键字 partial
                            if (classNode.Modifiers.Any(token => token.IsKind(SyntaxKind.PartialKeyword))
                                && IsRoslynProxyServiceCollection(typeSymbol))
                            {
                                var namespaceSymbol = typeSymbol.ContainingNamespace.OriginalDefinition as INamespaceSymbol;

                                if (namespaceSymbol != null)
                                {
                                    if (namespaceSymbol.IsGlobalNamespace)
                                    {
                                        containingNamespace = string.Empty;
                                    }
                                    else
                                    {
                                        containingNamespace = namespaceSymbol.ToDisplayString();
                                    }

                                }
                                else
                                {
                                    containingNamespace = string.Empty;
                                }

                                className = typeSymbol.Name;
                                hasCollection = true;
                            }
                        }
                    }
                }
            }

            // 使用了代理服务，才会返回构建信息
            if (hasCollection && proxyServices.Any())
            {
                buildInfo = new ProxyServiceTypeBuildInfo(containingNamespace, className, proxyServices);
                return true;
            }

            buildInfo = null!;
            return false;
        }

        /// <summary>
        /// 尝试获取代理类型信息
        /// </summary>
        bool TryGetProxyServiceInfo(INamedTypeSymbol namedTypeSymbol, out ProxyServiceInfo info)
        {
            var baseType = namedTypeSymbol.BaseType;

            if (baseType != null)
            {
                // 必须严格处理代理服务类型的基类
                if (baseType.OriginalDefinition.ToString() == "RoslynProxy.Core.RoslynDispatchProxy<TProxyService, TService, TImplementation>")
                {
                    var attrubutes = namedTypeSymbol.GetAttributes();

                    if (attrubutes.Any(item => item.ToString() == ScopedRoslynProxyAttributeName))
                    {
                        info = new ProxyServiceInfo(
                            baseType.TypeArguments[0].ToDisplayString(),
                            baseType.TypeArguments[2].ToDisplayString(),
                            ServiceScopes.Scoped);
                    }
                    else if (attrubutes.Any(item => item.ToString() == SingletonRoslynProxyAttributeName))
                    {
                        info = new ProxyServiceInfo(
                          baseType.TypeArguments[0].ToDisplayString(),
                          baseType.TypeArguments[2].ToDisplayString(),
                          ServiceScopes.Singleton);
                    }
                    else
                    {
                        info = new ProxyServiceInfo(
                          baseType.TypeArguments[0].ToDisplayString(),
                          baseType.TypeArguments[2].ToDisplayString(),
                          ServiceScopes.Transient);
                    }

                    return true;
                }

                baseType = baseType.BaseType;
            }

            info = null!;
            return false;
        }

        /// <summary>
        /// 判断类型是否是代理收集集合
        /// </summary>
        bool IsRoslynProxyServiceCollection(INamedTypeSymbol namedTypeSymbol)
        {
            var baseType = namedTypeSymbol.BaseType;

            if (baseType != null)
            {
                if (baseType.OriginalDefinition.ToString() == "RoslynProxy.Core.RoslynProxyServiceCollection")
                {
                    return true;
                }
            }

            return false;
        }
    }
}
